package model

import (
	"devOpsApi/common/util"
	"devOpsApi/libs"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/bitly/go-simplejson"
	"github.com/jinzhu/gorm"
	"math"
	"time"
)

type Pipeline struct {
	gorm.Model
	db               *gorm.DB
	Title            string      `gorm:"column:title" json:"title,omitempty" form:"title" validate:"required"`
	Name             string      `gorm:"column:name" json:"name,omitempty" form:"name" validate:"required"`
	Language         string      `gorm:"column:language" json:"language,omitempty" form:"language" validate:"required"`
	CustomLanguage   string      `gorm:"column:custom_language" json:"custom_language,omitempty" form:"custom_language"`
	CustomAccount    string      `gorm:"column:custom_account" json:"custom_account,omitempty" form:"custom_account"`
	CustomPwd        string      `gorm:"column:custom_pwd" json:"custom_pwd,omitempty" form:"custom_pwd"`
	CustomGitUrl     string      `gorm:"column:custom_git_url" json:"custom_git_url" form:"custom_git_url"`
	Version          string      `gorm:"column:version" json:"version,omitempty" form:"version"`
	Monitor          string      `gorm:"column:monitor" json:"monitor,omitempty" form:"monitor" validate:"required"`
	Coderepo         string      `gorm:"column:codeRepo" json:"codeRepo,omitempty" form:"codeRepo" validate:"required"`
	Branch           string      `gorm:"column:branch" json:"branch,omitempty" form:"branch" validate:"required"`
	Pid              uint        `gorm:"column:pid" json:"pid,omitempty" form:"pid" validate:"required"`
	Envs             interface{} `gorm:"column:envs" json:"envs,omitempty" form:"envs" validate:"required"`
	SettingFile      interface{} `gorm:"column:setting_file" json:"setting_file" form:"setting_file" validate:"required"`
	Flow             interface{} `gorm:"column:flow" json:"flow,omitempty" form:"flow" validate:"required"`
	HookId           uint        `gorm:"column:hook_id" json:"hook_id,omitempty" form:"-"`
	Uid              uint        `gorm:"column:uid" json:"uid,omitempty" form:"-"`
	ManageUid        string      `gorm:"column:manage_uid" json:"manage_uid,omitempty" form:"manage_uid"`
	LastBuild        *time.Time  `gorm:"column:last_build" json:"last_build" form:"-"`
	SuccessNum       uint        `gorm:"column:success_num" json:"success_num,omitempty" form:"-"`
	ErrorNum         uint        `gorm:"column:error_num" json:"error_num,omitempty" form:"-"`
	BuildState       string      `gorm:"column:build_state" json:"build_state,omitempty" form:"-"`
	ErrorMsg         string      `gorm:"column:error_msg" json:"error_msg,omitempty" form:"-"`
	GitLabSecret     string      `gorm:"column:gitlabsecret" json:"gitlabsecret" form:"-"`
	NextAction       string      `gorm:"column:next_action" json:"next_action" form:"next_action"`
	SettingConsulUrl string      `gorm:"column:setting_consul_url" json:"setting_consul_url" form:"setting_consul_url"`
	SettingConsul    interface{} `gorm:"column:setting_consul" json:"setting_consul" form:"setting_consul"`
	StartCallbackUrl string      `gorm:"column:start_callback_url" json:"start_callback_url" form:"start_callback_url"`
	CallbackUrl      string      `gorm:"column:callback_url" json:"callback_url" form:"callback_url"`
	CronSetting      interface{} `gorm:"column:cron_setting" json:"cron_setting" form:"cron_setting"`
	SendEmail        string      `gorm:"column:send_email" json:"send_email" form:"send_email"`
}

type PipelineList struct {
	Pipeline
	RealName      string `gorm:"column:realName" json:"realName,omitempty" form:"-"`
	ContainerName string `gorm:"column:-" json:"containerName,omitempty" form:"-"`
}

func (Pipeline) TableName() string {
	return "pipeline"
}

func NewPipeline() *Pipeline {
	return &Pipeline{}
}

func (m *Pipeline) List(uid uint, page, pagesize uint, k string) ([]PipelineList, uint, uint) {
	db := libs.DB
	var data = []PipelineList{}
	var totalCount uint
	limit := pagesize
	offset := (page - 1) * limit
	fields := "pipeline.id,pipeline.title,pipeline.name,pipeline.language,pipeline.custom_language,pipeline.version,pipeline.monitor,pipeline.codeRepo,pipeline.branch,pipeline.custom_git_url,pipeline.pid,pipeline.uid,pipeline.last_build,pipeline.success_num,pipeline.error_num,pipeline.error_msg,pipeline.created_at,pipeline.updated_at,pipeline.build_state,pipeline.gitlabsecret,pipeline.next_action,pipeline.manage_uid,user.realName"
	if k != "" {
		db = db.Where("pipeline.title LIKE ? or pipeline.name LIKE ? ", "%"+k+"%", "%"+k+"%")
	}
	db = m.adminCheck(uid, db)
	db.Model(&Pipeline{}).Count(&totalCount)
	err := db.Model(&Pipeline{}).Select(fields).Joins("left join user on pipeline.uid = user.id").Offset(offset).Limit(limit).Order("id desc").Find(&data).Error
	if err != nil {
		//log.Fatalln(err)
	}
	totalPages := uint(math.Ceil(float64(totalCount) / float64(limit)))
	return data, totalCount, totalPages
}

func (m *Pipeline) ListAll(uid uint) []Pipeline {
	db := libs.DB
	var data = []Pipeline{}
	db = m.adminCheck(uid, db)
	err := db.Model(&Pipeline{}).Model(&Pipeline{}).Select("id,name,language,custom_language,version,monitor,codeRepo,branch,pid,uid,last_build,success_num,error_num,error_msg,created_at,updated_at,build_state,gitlabsecret,next_action,manage_uid").Order("id desc").Find(&data).Error
	if err != nil {
		//log.Fatalln(err)
	}
	return data
}

func (m *Pipeline) ListCondition(where time.Time) []Pipeline {
	db := libs.DB
	var data = []Pipeline{}
	err := db.Model(&Pipeline{}).Select("id,name,language,custom_language,version,monitor,codeRepo,branch,pid,uid,last_build,success_num,error_num,error_msg,created_at,updated_at,build_state,gitlabsecret,manage_uid").Where("last_build < ?", where).Order("id desc").Find(&data).Error
	if err != nil {
		//log.Fatalln(err)
	}
	return data
}

func (m *Pipeline) ListAllCron() []Pipeline {
	db := libs.DB
	var data = []Pipeline{}
	err := db.Model(&Pipeline{}).Model(&Pipeline{}).Select("id,name,cron_setting,monitor").Where("monitor = ? ", "cron").Order("id desc").Find(&data).Error
	if err != nil {
		//log.Fatalln(err)
	}
	return data
}

func (m *Pipeline) Create(uid uint) (Pipeline, error) {
	db := libs.DB
	var pipeline Pipeline
	if !db.Where("name = ?", m.Name).Find(&Pipeline{}).RecordNotFound() {
		return pipeline, errors.New("该名称已经存在")
	}
	Envs, _ := json.Marshal(m.Envs)
	Flow, _ := json.Marshal(m.Flow)
	SettingFile, _ := json.Marshal(m.SettingFile)
	SettingConsul, _ := json.Marshal(m.SettingConsul)
	m.Envs = string(Envs)
	m.Flow = string(Flow)
	m.SettingFile = string(SettingFile)
	m.SettingConsul = string(SettingConsul)
	m.Uid = uid

	if err := util.Validate(m); err != nil {
		return pipeline, err
	}
	if err := db.Create(m).Error; err != nil {
		return pipeline, err
	}
	return *m, nil
}

func (m *Pipeline) Copy(uid uint, pipeline Pipeline) (Pipeline, error) {
	db := libs.DB
	pipeline.Name = fmt.Sprintf("%s_bak", pipeline.Name)
	pipeline.Title = fmt.Sprintf("%s_bak", pipeline.Title)
	if !db.Where("name = ?", pipeline.Name).Find(&Pipeline{}).RecordNotFound() {
		return pipeline, errors.New("该名称已经存在")
	}
	pipeline.ID = 0
	pipeline.HookId = 0
	pipeline.LastBuild = nil
	pipeline.SuccessNum = 0
	pipeline.ErrorNum = 0
	pipeline.BuildState = ""
	pipeline.ErrorMsg = ""
	pipeline.NextAction = ""
	pipeline.CreatedAt = time.Now()
	pipeline.UpdatedAt = time.Now()
	if err := db.Create(&pipeline).Error; err != nil {
		return pipeline, err
	}
	return pipeline, nil
}

func (m *Pipeline) Update(uid uint) error {
	db := libs.DB
	var pipeline Pipeline
	if !db.Where("name = ? and id != ?", m.Name, m.ID).First(&Pipeline{}).RecordNotFound() {
		return errors.New("该名称已经存在")
	}

	db = db.Where("id = ?", m.ID)
	db = m.adminCheck(uid, db)
	if err := db.First(&pipeline).RecordNotFound(); err {
		return errors.New("未找到项目")
	}

	m.Uid = pipeline.Uid
	Envs, _ := json.Marshal(m.Envs)
	Flow, _ := json.Marshal(m.Flow)
	SettingFile, _ := json.Marshal(m.SettingFile)
	SettingConsul, _ := json.Marshal(m.SettingConsul)
	m.Envs = string(Envs)
	m.Flow = string(Flow)
	m.SettingFile = string(SettingFile)
	m.SettingConsul = string(SettingConsul)

	if err := util.Validate(m); err != nil {
		return err
	}
	return db.Save(m).Error
}

func (m *Pipeline) UpdateConfig(uid uint, id uint, configName, configValue string) error {
	db := libs.DB
	var pipeline Pipeline
	db = db.Model(&Pipeline{}).Where("id = ?", id)
	db = m.adminCheck(uid, db)
	if err := db.First(&pipeline).RecordNotFound(); err {
		return errors.New("未找到项目")
	}
	err := db.Update(configName, configValue).Error
	if err != nil {
		return err
	}
	return nil
}

func (m *Pipeline) Del(uid uint, id uint) error {
	db := libs.DB
	db = db.Where("id = ?", id)
	db = m.adminCheck(uid, db)
	return db.Delete(m).Error
}

func (m *Pipeline) DelReal(uid uint, id uint) error {
	db := libs.DB
	db = db.Where("id = ?", id)
	db = m.adminCheck(uid, db)
	return db.Unscoped().Delete(m).Error
}

func (m *Pipeline) Detail(uid uint, id uint) (pipeline Pipeline, err error) {
	db := libs.DB
	db = db.Where("id = ?", id)
	db = m.adminCheck(uid, db)
	err = db.First(&pipeline).Error
	return
}

func (m *Pipeline) DetailByName(uid uint, name string) (pipeline Pipeline, err error) {
	db := libs.DB
	db = db.Where("`name` = ?", name)
	db = m.adminCheck(uid, db)
	err = db.First(&pipeline).Error
	return
}

func (m *Pipeline) DetailAnyOne(id uint) (pipeline Pipeline, err error) {
	db := libs.DB
	err = db.Where("id = ?", id).First(&pipeline).Error
	return
}

func (m *Pipeline) UpdateFields(uid uint, id uint, fields map[string]interface{}) error {
	db := libs.DB
	db = db.Where("id = ?", id)
	db = m.adminCheck(uid, db)
	return db.Model(&Pipeline{}).Updates(fields).Error
}

func (m *Pipeline) UpdateFieldsAnyOne(id uint, fields map[string]interface{}) error {
	db := libs.DB
	return db.Model(&Pipeline{}).Where("id = ?", id).Updates(fields).Error
}

func (m *Pipeline) UpdateSuccessNum(uid uint, id uint) error {
	db := libs.DB
	db = db.Where("id = ?", id)
	db = m.adminCheck(uid, db)
	return db.Model(&Pipeline{}).UpdateColumn("success_num", gorm.Expr("success_num + ?", 1)).Error
}

func (m *Pipeline) UpdateErrNum(uid uint, id uint) error {
	db := libs.DB
	db = db.Where("id = ?", id)
	db = m.adminCheck(uid, db)
	return db.Model(&Pipeline{}).UpdateColumn("error_num", gorm.Expr("error_num + ?", 1)).Error
}

func (m *Pipeline) adminCheck(uid uint, db *gorm.DB) *gorm.DB {
	roleUser, _ := NewUserRoleUser().GetUserRole(int(uid))
	if roleUser.RoleId != 1 {
		db = db.Where("uid = ? or find_in_set(?,manage_uid)", uid, uid)
	}
	return db
}

func (m *Pipeline) GetConfigEnvBy(uid, id uint, configName string) string {
	var pipeline Pipeline
	db := libs.DB
	db = db.Where("id = ?", id)
	db = m.adminCheck(uid, db)
	err := db.First(&pipeline).Error
	if err != nil {
		return err.Error()
	}

	if string(pipeline.Envs.([]byte)) == "" {
		return err.Error()
	}

	EnvsJson, err := simplejson.NewJson(pipeline.Envs.([]byte))
	if err != nil {
		return err.Error()
	}

	envList, err := EnvsJson.Array()
	if err != nil {
		return err.Error()
	}

	if len(envList) == 0 {
		return ""
	}

	for key, val := range envList {
		valMap := val.(map[string]interface{})
		if _, ok := valMap["name"]; ok && valMap["name"] == configName { //存在Key值
			val, err := EnvsJson.GetIndex(key).Get("value").String()
			if err != nil {
				return err.Error()
			}
			return val
		}
	}

	return ""
}

func (m *Pipeline) SetConfigEnvBy(uid, id uint, configName, configValue string) error {
	var pipeline Pipeline
	db := libs.DB
	db = db.Where("id = ?", id)
	db = m.adminCheck(uid, db)
	err := db.First(&pipeline).Error
	if err != nil {
		return err
	}

	if string(pipeline.Envs.([]byte)) == "" {
		return errors.New("envs byte is empty")
	}

	EnvsJson, err := simplejson.NewJson(pipeline.Envs.([]byte))
	if err != nil {
		return err
	}

	envList, err := EnvsJson.Array()
	if err != nil {
		return err
	}

	if len(envList) == 0 {
		return errors.New("envs is empty")
	}

	for key, val := range envList {
		valMap := val.(map[string]interface{})
		if _, ok := valMap["name"]; ok && valMap["name"] == configName { //存在Key值
			EnvsJson.GetIndex(key).Set("value", configValue)
		}
	}

	NewEnvstr, err := EnvsJson.Encode()
	if err != nil {
		return err
	}
	NewEnvstr = util.TransHtmlJson(NewEnvstr)
	err = db.Model(&Pipeline{}).Update("envs", string(NewEnvstr)).Error
	if err != nil {
		return err
	}
	return nil
}
